//============================================================================
// Name        : cpp_bmp2png.cpp
// Author      : 
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>
#include "lodepng/lodepng.h"
#include "EasyBMP/EasyBMP.h"
using namespace std;

const string BMP_EXTENTION = ".bmp";
const string PNG_EXTENTION = ".png";

//returns 0 if all went ok, non-0 if error
//output image is always given in RGBA (with alpha channel), even if it's a BMP without alpha channel
unsigned decodeBMP(vector<unsigned char>& image, unsigned& w, unsigned& h, const vector<unsigned char>& bmp)
{
  static const unsigned MINHEADER = 54; //minimum BMP header size

  if(bmp.size() < MINHEADER) return -1;
  if(bmp[0] != 'B' || bmp[1] != 'M') return 1; //It's not a BMP file if it doesn't start with marker 'BM'
  unsigned pixeloffset = bmp[10] + 256 * bmp[11]; //where the pixel data starts
  //read width and height from BMP header
  w = bmp[18] + bmp[19] * 256;
  h = bmp[22] + bmp[23] * 256;
  //read number of channels from BMP header
  if(bmp[28] != 24 && bmp[28] != 32) return 2; //only 24-bit and 32-bit BMPs are supported.
  unsigned numChannels = bmp[28] / 8;

  //The amount of scanline bytes is width of image times channels, with extra bytes added if needed
  //to make it a multiple of 4 bytes.
  unsigned scanlineBytes = w * numChannels;
  if(scanlineBytes % 4 != 0) scanlineBytes = (scanlineBytes / 4) * 4 + 4;

  unsigned dataSize = scanlineBytes * h;
  if(bmp.size() < dataSize + pixeloffset) return 3; //BMP file too small to contain all pixels

  image.resize(w * h * 4);

  /*
  There are 3 differences between BMP and the raw image buffer for LodePNG:
  -it's upside down
  -it's in BGR instead of RGB format (or BRGA instead of RGBA)
  -each scanline has padding bytes to make it a multiple of 4 if needed
  The 2D for loop below does all these 3 conversions at once.
  */
  for(unsigned y = 0; y < h; y++)
  for(unsigned x = 0; x < w; x++)
  {
    //pixel start byte position in the BMP
    unsigned bmpos = pixeloffset + (h - y - 1) * scanlineBytes + numChannels * x;
    //pixel start byte position in the new raw image
    unsigned newpos = 4 * y * w + 4 * x;
    if(numChannels == 3)
    {
      image[newpos + 0] = bmp[bmpos + 2]; //R
      image[newpos + 1] = bmp[bmpos + 1]; //G
      image[newpos + 2] = bmp[bmpos + 0]; //B
      image[newpos + 3] = 255;            //A
    }
    else
    {
      image[newpos + 0] = bmp[bmpos + 3]; //R
      image[newpos + 1] = bmp[bmpos + 2]; //G
      image[newpos + 2] = bmp[bmpos + 1]; //B
      image[newpos + 3] = bmp[bmpos + 0]; //A
    }
  }
  return 0;
}

unsigned decodeBMP2(vector<unsigned char>& image, unsigned& w, unsigned& h, BMP& bmp){
	w = bmp.TellWidth();
	h = bmp.TellHeight();

	image.resize(w * h * 4);

	/*
	  There are 3 differences between BMP and the raw image buffer for LodePNG:
	  -it's upside down
	  -it's in BGR instead of RGB format (or BRGA instead of RGBA)
	  -each scanline has padding bytes to make it a multiple of 4 if needed
	  The 2D for loop below does all these 3 conversions at once.
	  */
	  for(unsigned y = 0; y < h; y++)
	  for(unsigned x = 0; x < w; x++)
	  {
	    RGBApixel color = bmp.GetPixel(x, y);

	    if(bmp.TellBitDepth() <= 24) color.Alpha = 255;

		  //pixel start byte position in the new raw image
	    unsigned newpos = 4 * y * w + 4 * x;
	    image[newpos + 0] = color.Red; //R
		  image[newpos + 1] = color.Green; //G
		  image[newpos + 2] = color.Blue; //B
		  image[newpos + 3] = color.Alpha; //A
	  }
	  return 0;
}

int main() {
	string bmpFiles[] = {
			"1bit",
			"3bit",
			"4bit",
			"24bit"
	};

	vector<unsigned char> bmp;
  vector<unsigned char> image;
  unsigned w, h;
  unsigned error;
  BMP tmp;
  vector<unsigned char> png;

  for(unsigned int i=0;i<sizeof(bmpFiles)/sizeof(*bmpFiles);i++){
	  cout << bmpFiles[i] << " start convert!!!!" << endl;

	  lodepng::load_file(bmp, bmpFiles[i]+BMP_EXTENTION);
	  error = decodeBMP(image, w, h, bmp);

	  if(error)
	  {
		  tmp.ReadFromFile((bmpFiles[i]+BMP_EXTENTION).c_str());

			decodeBMP2(image, w, h, tmp);
	  }

	  error = lodepng::encode(png, image, w, h);

	  if(error)
	  {
		cout << "PNG encoding error " << error << ": " << lodepng_error_text(error) << endl;
		return 0;
	  }

	  lodepng::save_file(png, bmpFiles[i]+PNG_EXTENTION);

	  cout << bmpFiles[i] << " end convert!!!!" << endl;
  }

	return 0;
}
